import { HttpException, Injectable } from '@nestjs/common';
import { CreateUseDbDto } from './dto/create-use-db.dto';
import { UpdateUseDbDto } from './dto/update-use-db.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UseDb } from './entities/use-db.entity';
import { plainToInstance } from 'class-transformer';
import { IdCard } from './entities/id_card.entity';

@Injectable()
export class UseDbService {
  constructor(
    @InjectRepository(UseDb)
    private readonly useDbRepository: Repository<UseDb>,
    @InjectRepository(IdCard)
    private readonly idCardPePository: Repository<IdCard>,
  ) {}

  async findAll() {
    const useDbs = await this.useDbRepository.find();
    return useDbs.map((useDb) => plainToInstance(UseDb, useDb));
  }

  /**
   * 更新用户信息
   * @param id 用户ID
   * @param updateUseDbDto 更新用户信息DTO
   * @returns 更新后的用户信息
   */
  async updateOne(id: number, updateUseDbDto: UpdateUseDbDto) {
    const existUser = await this.useDbRepository.findOne({ where: { id } });
    if (!existUser) {
      throw new HttpException(`id为${id}的用户不存在`, 401);
    }
    // 合并更新数据
    const updateUser = this.useDbRepository.merge(existUser, updateUseDbDto);
    return this.useDbRepository.save(updateUser);
  }

  /**
   * 更新用户信息
   * @param id 用户ID
   * @param updateUseDbDto 更新用户信息DTO
   * @returns 更新后的用户信息
   */
  async update(id: number, updateUseDbDto: Partial<UpdateUseDbDto>) {
    //preload:  从给定的javascript对象创建一个新的实体。
    // 如果数据库中已经存在该实体，则它加载它（以及与它相关的一切），用给定对象的新值替换所有值并返回这个新实体。
    // 这个新实体实际上是从具有所有属性的db实体中加载的
    // 由新对象替换。
    // 注意，给定的类实体对象必须有一个实体id /主键来查找实体。
    // 如果没有找到给定id的实体，则返回未定义。
    const user = await this.useDbRepository.preload({
      id: id,
      ...updateUseDbDto,
    });
    if (!user) {
      throw new HttpException(`id为${id}的用户不存在`, 401);
    }
    return this.useDbRepository.save(user);
  }

  // 获取文章列表 分页1  也可参考 getActList
  async findAll_(query: {
    pageNum?: number;
    pageSize?: number;
    [key: string]: any;
  }): Promise<{
    list: UseDb[];
    total: number;
    totalPages: number;
    currentPage: number;
  }> {
    const { pageNum = 1, pageSize = 10, ...params } = query;
    const [posts, totalCount] = await this.useDbRepository.findAndCount({
      skip: (pageNum - 1) * pageSize, // 分页偏移量
      take: pageSize, // 每页显示的记录数
      order: { id: 'DESC' },
      where: {
        ...params,
      },
    });
    return {
      list: posts,
      total: totalCount,
      totalPages: Math.ceil(totalCount / pageSize), // 计算总页数
      currentPage: pageNum, // 当前页
    };
  }

  // 获取文章列表 分页2
  async findAllPagation(query) {
    try {
      const qb = await this.useDbRepository.createQueryBuilder('user');
      qb.orderBy('user.id', 'DESC');

      const total = await qb.getCount();
      const { pageNum = 1, pageSize = 10, ...params } = query;

      qb.limit(pageSize);
      qb.offset(pageSize * (pageNum - 1));
      qb.where(params);
      const users = await qb.getMany();
      return {
        list: users.map((v) => plainToInstance(UseDb, v)),
        total: total,
      };
    } catch (err) {
      throw new HttpException('查询参数错误', 401);
    }
  }

  async findIdCardOne(id: number) {
    const idcard = await this.idCardPePository.findOne({
      where: {
        id: id,
      },
      relations: {
        // 主表关联子表 ， 从idcard 中关联出 user 信息
        user: true,
      },
    });
    if (!idcard) {
      throw new Error('没有找到相应的记录');
    }
    return plainToInstance(IdCard, idcard);
  }

  // 使用 plainToInstance 函数将数据库返回的普通对象转换为 UseDb 类的实例，
  // 这样 @Exclude() 装饰器才能生效，密码字段才会在响应中被排除。
  async findOne(id: number) {
    // 方式1
    const user1 = await this.useDbRepository.manager.query(
      `SELECT user.* FROM store_user AS user WHERE user.id = ${id}`,
    );
    console.log(plainToInstance(UseDb, user1), 111);

    // 方式2
    // transaction 包裹一层事务的 sql
    await this.useDbRepository.manager.transaction(async (manager) => {
      const user2 = await manager.findOne(UseDb, {
        where: {
          id: id,
        },
        relations: {
          idCard: true, // 添加关联加载 ， 子表关联主表，从子表中关联出主表信息
        },
      });
      console.log(plainToInstance(UseDb, user2), 222);
    });

    // 方式3
    // 直接使用 query builder 进行复杂 sql 语句的查询
    const queryBuilder =
      await this.useDbRepository.manager.createQueryBuilder(); //：创建复杂 sql 语句，比如 join 多个 Entity 的查询
    const user3 = await queryBuilder
      .select('user')
      .from(UseDb, 'user')
      .leftJoinAndSelect('user.idCard', 'idCard') // 添加关联加载
      .where('user.id = :id', { id: id })
      .getOne();
    console.log(user3, 333);

    // 方式4
    const user = await this.useDbRepository.findOne({
      where: {
        id: id,
      },
      relations: {
        idCard: true, // 添加关联加载
      },
    });

    console.log(plainToInstance(UseDb, user), 444);

    if (!user) {
      throw new Error('没有找到相应的记录');
    }
    return plainToInstance(UseDb, user);
  }

  async create(createUseDbDto: Partial<CreateUseDbDto>) {
    return this.useDbRepository.save(createUseDbDto);
  }

  async remove(id: number) {
    const user = await this.useDbRepository.findOne({
      where: {
        id: id,
      },
    });
    if (!user) {
      throw new Error('没有找到相应的记录');
    }
    return this.useDbRepository.remove(user);
  }
}
